﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
namespace Smark.Core
{
    public delegate object SmarkActivatorHandler(object[] parameters);
    public class SmarkActivator
    {
        public static T CreateInstance<T>()
        {
            return CreateInstance<T>(null);
        }
        static Dictionary<string, SmarkActivatorHandler> mHandlers = new Dictionary<string, SmarkActivatorHandler>();

        public static T CreateInstance<T>(params object[] args)
        {

            return (T)CreateInstance(typeof(T), args);
        }
        public static object CreateInstance(Type type)
        {
            return CreateInstance(type, null);
        }
        public static object CreateInstance(Type type, params object[] args)
        {
            Type objtype = type;
            string key;
            Type[] ptypes = GetParameterTypes(out key, args);

            key = type.FullName + "_" + key;
            if (!mHandlers.ContainsKey(key))
            {
                CreateHandler(objtype, key, ptypes);
            }
            
            return mHandlers[key](args);
        }
        static void CreateHandler(Type objtype, string key, Type[] ptypes)
        {
            lock (typeof(SmarkActivator))
            {
                if (!mHandlers.ContainsKey(key))
                {
                    DynamicMethod dm = new DynamicMethod("M_"+Guid.NewGuid().ToString("N"), typeof(object), new Type[] { typeof(object[]) }, typeof(SmarkActivatorHandler).Module);
                    ILGenerator il = dm.GetILGenerator();
                    ConstructorInfo cons = objtype.GetConstructor(ptypes);

                    il.Emit(OpCodes.Nop);
                    for (int i = 0; i < ptypes.Length; i++)
                    {
                        il.Emit(OpCodes.Ldarg_0);
                        il.Emit(OpCodes.Ldc_I4, i);
                        il.Emit(OpCodes.Ldelem_Ref);
                        if (ptypes[i].IsValueType)
                        {
                            il.Emit(OpCodes.Unbox_Any, ptypes[i]);
                        }
                        else
                        {
                            il.Emit(OpCodes.Castclass, ptypes[i]);
                        }


                    }
                    il.Emit(OpCodes.Newobj, cons);
                    il.Emit(OpCodes.Ret);
                    SmarkActivatorHandler ci = (SmarkActivatorHandler)dm.CreateDelegate(typeof(SmarkActivatorHandler));
                    mHandlers.Add(key, ci);

                }
            }
        }
        static Type[] GetParameterTypes(out string parameterkey, params object[] parameters)
        {
            parameterkey = "";
            if (parameters == null || parameters.Length ==0)
                return new Type[0];
            Type[] values = new Type[parameters.Length];
            switch (parameters.Length)
            {
                case 1:
                    values[0] = parameters[0].GetType();
                    parameterkey = values[0].Name;
                    
                    break;
                case 2:
                    values[0] = parameters[0].GetType();
                    values[1] = parameters[1].GetType();
                    parameterkey = string.Concat(values[0], values[1]);
                    break;
                case 3:
                    values[0] = parameters[0].GetType();
                    values[1] = parameters[1].GetType();
                    values[2] = parameters[2].GetType();
                    parameterkey = string.Concat(values[0], values[1],values[3]);
                    break;
                default:
                    for (int i = 0; i < parameters.Length; i++)
                    {
                        values[i] = parameters[i].GetType();
                    }
                    parameterkey = string.Concat(values);
                    break;
            }
            return values;
        }

       
       
    }
}
